package no.uis.archiva.consumer.liferay;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.codehaus.plexus.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

public class LiferayPluginPackage {

  static final String DEFAULT_LIFERAY_VERSION = "5.2.3";
  private String name;
  private Set<LiferayLicense> licenses = new HashSet<LiferayLicense>();
  private String author;
  private String moduleGroupId;
  private String changeLog;
  private String pageUrl;
  private String shortDescription;
  private String moduleIncrementalVersion = "0";
  private String liferayVersion;
  private Set<String> tags = Collections.emptySet();
  private Set<String> types = Collections.emptySet();
  private Element xmlElem;
  private String moduleId;
  
  private static Logger log = LoggerFactory.getLogger(LiferayPluginPackage.class);
  
  private LiferayPluginPackage(String liferayVersion) {
    setLiferayVersion(liferayVersion);
  }
  
  public static LiferayPluginPackage readProperties(Properties props) throws IOException {
    LiferayPluginPackage pack = new LiferayPluginPackage(props.getProperty("liferay-version"));

    pack.setName(props.getProperty("name"));
    pack.addLicense(LiferayLicense.buildLicense(props.getProperty("licenses")));
    pack.setAuthor(props.getProperty("author"));
    pack.setModuleGroupId(props.getProperty("module-group-id"));
    pack.setChangeLog(props.getProperty("change-log"));
    pack.setPageUrl(props.getProperty("page-url"));
    pack.setShortDescription(props.getProperty("short-description"));
    pack.setModuleIncrementalVersion(props.getProperty("module-incremental-version"));
    pack.setLiferayVersion(props.getProperty("liferay-version"));
    Set<String> s = new HashSet<String>();
    addToCollection(s, props.getProperty("tags"));
    pack.setTags(s);
    s = new HashSet<String>();
    addToCollection(s, props.getProperty("types"));
    pack.setTypes(s);
    return pack;
  }

  public static LiferayPluginPackage readXml(Element root) {
    
    LiferayPluginPackage pack = new LiferayPluginPackage(DEFAULT_LIFERAY_VERSION);

    XPathFactory xpathFactory = javax.xml.xpath.XPathFactory.newInstance();
    XPath xp = xpathFactory.newXPath();
    
    try {
      pack.setModuleId(xp.evaluate("//plugin-package/module-id", root));
      pack.xmlElem = root;
    } catch(XPathExpressionException e) {
      log.error("create package from DOM element", e);
      return null;
    }
    
    return pack; 
  }
  
  public Element toNode(Document doc) throws ParserConfigurationException, IOException, TransformerException, SAXException {
    if (this.xmlElem == null) {
      Element pluginPackageNode = doc.createElement("plugin-package");
  
      XmlHelper.appendTextElement(pluginPackageNode, "name", getName());
  
      XmlHelper.appendTextElement(pluginPackageNode, "module-id", getModuleId());
      
      XmlHelper.appendListElement(pluginPackageNode, "type", getTypes());
      
      XmlHelper.appendTextElement(pluginPackageNode, "short-description", getShortDescription());
      
      XmlHelper.appendTextElement(pluginPackageNode, "change-log", getChangeLog());
      
      XmlHelper.appendTextElement(pluginPackageNode, "page-url", getPageUrl());
      
      XmlHelper.appendTextElement(pluginPackageNode, "author", getAuthor());
      
      XmlHelper.appendListElement(pluginPackageNode, "liferay-version", getLiferayVersionList());
      
      XmlHelper.appendListElement(pluginPackageNode, "tag", getTags());
      
      appendLicenseNode(pluginPackageNode);
      return pluginPackageNode;
    } else {
      Node n = doc.importNode(xmlElem, true);
      return (Element)n;
    }
  }

  private static void addToCollection(Collection<String> coll, String valueList) {
    if (valueList == null || valueList.isEmpty()) {
      return;
    }
    Collections.addAll(coll, valueList.split("\\w*,\\w"));
  }
  
  private List<String> getLiferayVersionList() {
    return Arrays.asList(getLiferayVersion()+'+');
  }

  private Element appendLicenseNode(Node parent) {
    if (licenses == null || licenses.isEmpty()) {
      return null;
    }
    Element licensesNode = XmlHelper.appendChildElement(parent, "licenses");
    for (LiferayLicense license : this.licenses) {
      Element licenseNode = XmlHelper.appendTextElement(licensesNode, "license", license.getName());
      XmlHelper.addAttribute(licenseNode, "osi-approved", license.isOsiApproved());
      XmlHelper.addAttribute(licenseNode, "url", license.getUrl());
    }
    
    return licensesNode;
  }

  public Set<String> getTypes() {
    return this.types;
  }

  private void setTypes(Set<String> types) {
    this.types = Collections.unmodifiableSet(types);
  }

  private void setModuleId(String moduleId) {
    this.moduleId = moduleId;
  }
  
  public String getModuleId() {
    if (moduleId == null) {
      moduleId = String.format("%s/%s/%s.%s/war", getModuleGroupId(), getName(), getLiferayVersion(), getModuleIncrementalVersion());
    }
    return moduleId;
  }

  public String getName() {
    return name;
  }

  private void setName(String name) {
    this.name = name;
  }

  public Set<LiferayLicense> getLicenses() {
    return licenses;
  }

  private void addLicense(LiferayLicense liferayLicense) {
    licenses.add(liferayLicense);
  }

  public String getAuthor() {
    return author;
  }

  private void setAuthor(String author) {
    this.author = author;
  }

  public String getModuleGroupId() {
    return moduleGroupId;
  }

  private void setModuleGroupId(String moduleGroupId) {
    this.moduleGroupId = moduleGroupId;
  }

  public String getChangeLog() {
    return changeLog;
  }

  private void setChangeLog(String changeLog) {
    this.changeLog = changeLog;
  }

  public String getPageUrl() {
    return pageUrl;
  }

  private void setPageUrl(String pageUrl) {
    this.pageUrl = pageUrl;
  }

  public String getShortDescription() {
    return shortDescription;
  }

  private void setShortDescription(String shortDescription) {
    this.shortDescription = shortDescription;
  }

  private String getModuleIncrementalVersion() {
    return moduleIncrementalVersion;
  }

  private void setModuleIncrementalVersion(String moduleIncrementalVersion) {
    if (StringUtils.isNumeric(moduleIncrementalVersion)) {
      this.moduleIncrementalVersion = moduleIncrementalVersion;
    }
  }

  public String getLiferayVersion() {
    if (liferayVersion == null) {
      liferayVersion = DEFAULT_LIFERAY_VERSION;
    }
    return liferayVersion;
  }

  private void setLiferayVersion(String liferayVersion) {
    this.liferayVersion = liferayVersion;
  }

  public Set<String> getTags() {
    return tags;
  }

  private void setTags(Set<String> tags) {
    this.tags = Collections.unmodifiableSet(tags);
  }
}
